import {
  CSSProperties,
  ReactNode,
  useLayoutEffect,
  useRef,
  useState,
} from "react";
import style from "./Collapse.module.css";
import ArrowRightIcon from "../assets/icons/arrow-right.svg";
import clsx from "clsx";

type CollapseProps = {
  renderTitle: () => ReactNode;
  renderContent: () => ReactNode;
  className?: string;
  style?: CSSProperties;
};

export default function Collapse(props: CollapseProps) {
  const { renderTitle, renderContent, className, style: _style } = props;
  const [isShow, setIsShow] = useState(false);
  const [isMount, setIsMount] = useState(isShow);
  const contentRef = useRef<HTMLDivElement>(null);
  const pendingCallbackRef = useRef<() => void>();
  useLayoutEffect(() => {
    if (isShow) {
      setIsMount(true);
      requestAnimationFrame(() => {
        const el = contentRef.current!;
        if (pendingCallbackRef.current) {
          el.removeEventListener("transitionend", pendingCallbackRef.current);
          pendingCallbackRef.current = function () {
            el.removeEventListener(
              "transitionend",
              pendingCallbackRef.current!
            );
            pendingCallbackRef.current = undefined;
            el.style.height = "";
            el.style.transition = "";
          };
          el.addEventListener("transitionend", pendingCallbackRef.current);
          el.style.height = el.scrollHeight + "px";
        } else {
          const height = el.clientHeight;
          el.style.height = 0 + "px";
          requestAnimationFrame(() => {
            el.style.transition = "height 300ms ease";
            el.style.height = height + "px";
          });
          pendingCallbackRef.current = function () {
            el.removeEventListener(
              "transitionend",
              pendingCallbackRef.current!
            );
            pendingCallbackRef.current = undefined;
            el.style.transition = "";
            el.style.height = "";
          };
          el.addEventListener("transitionend", pendingCallbackRef.current);
        }
      });
    } else if (isMount) {
      const el = contentRef.current!;
      if (pendingCallbackRef.current) {
        el.removeEventListener("transitionend", pendingCallbackRef.current);
        pendingCallbackRef.current = function () {
          el.removeEventListener("transitionend", pendingCallbackRef.current!);
          pendingCallbackRef.current = undefined;
          setIsMount(false);
        };
        el.addEventListener("transitionend", pendingCallbackRef.current);
        el.style.height = "0px";
      } else {
        const height = el.clientHeight;
        el.style.height = height + "px";
        requestAnimationFrame(() => {
          el.style.height = "0px";
          el.style.transition = "height 300ms ease";
        });
        pendingCallbackRef.current = function () {
          el.removeEventListener("transitionend", pendingCallbackRef.current!);
          pendingCallbackRef.current = undefined;
          setIsMount(false);
        };
        el.addEventListener("transitionend", pendingCallbackRef.current);
      }
    }
  }, [isShow]); // eslint-disable-line

  return (
    <div className={clsx(style.collapse, className)} style={_style}>
      <div className={style.title} onClick={() => setIsShow(!isShow)}>
        <div className={style.iconBox}>
          <ArrowRightIcon className={clsx(style.icon, isShow && style.open)} />
        </div>
        <div className={style.titleContent}>{renderTitle()}</div>
      </div>
      {isMount && (
        <div className={style.content} ref={contentRef}>
          {renderContent()}
        </div>
      )}
    </div>
  );
}
